package com.readingtree.library.security;

import com.readingtree.library.domain.LibraryResource;
import com.readingtree.library.domain.LibraryRole;
import com.readingtree.library.domain.repository.LibraryResourceRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 最核心的地方，就是提供某个资源对应的权限定义，即getAttributes方法返回的结果。 此类在初始化时，应该取到所有资源及其对应角色的定义。
 */
@Service
public class CustomInvocationSecurityMetadataSourceService implements
        FilterInvocationSecurityMetadataSource {

    private final static Logger LOGGER = LoggerFactory.getLogger(CustomInvocationSecurityMetadataSourceService.class);
    private static final String RESOURCE_MAP = "resourceMap";
    @Autowired
    private LibraryResourceRepository resourceRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct
    private void loadResourceDefine() {
        Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<>();
        Iterable<LibraryResource> libraryResources = resourceRepository.findAll();
        Iterator<LibraryResource> libraryResourceIterator = libraryResources.iterator();
        while (libraryResourceIterator.hasNext()) {
            LibraryResource resource = libraryResourceIterator.next();
            String path = resource.getMethodPath();
            LibraryRole role = resource.getRole();
            Collection<ConfigAttribute> attributes = null;
            if (resourceMap.get(path) != null) {
                attributes = resourceMap.get(path);
                boolean existed = false;
                for (ConfigAttribute attribute : attributes) {
                    if (attribute.getAttribute().equals(role)) {
                        existed = true;
                    }
                }
                if (!existed) {
                    ConfigAttribute ca = new SecurityConfigEnhance(role.getName());
                    attributes.add(ca);
                }
            } else {
                attributes = new ArrayList<ConfigAttribute>();
                ConfigAttribute ca = new SecurityConfigEnhance(role.getName());
                attributes.add(ca);
            }
            if (attributes != null) {
                resourceMap.put(path, attributes);
            }
        }
        if (resourceMap != null) {
            redisTemplate.opsForHash().putAll(RESOURCE_MAP, resourceMap);
        }
        LOGGER.info("LOAD ALL AUTHORITY FINISHED , THE RESOUCE COUNT IS " + resourceMap.size());
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return new ArrayList<ConfigAttribute>();
    }

    // 根据URL，找到相关的权限配置
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {
        LOGGER.info("invoke get attributes ,the request url is " + object.toString());
        // object 是一个URL，被用户请求的url。</span>
        FilterInvocation filterInvocation = (FilterInvocation) object;
        Map<String, Collection<ConfigAttribute>> resourceMap = redisTemplate.opsForHash().entries(RESOURCE_MAP);
        if (resourceMap == null || resourceMap.size() == 0) {
            loadResourceDefine();
            resourceMap = redisTemplate.opsForHash().entries(RESOURCE_MAP);
        }
        if (resourceMap != null) {
            Iterator<String> ite = resourceMap.keySet().iterator();
            while (ite.hasNext()) {
                String resURL = ite.next();
                LOGGER.info("URL IS " + resURL);
                RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL);
                if (requestMatcher.matches(filterInvocation.getHttpRequest())) {
                    return resourceMap.get(resURL);
                }
            }
        }
        return null;
    }

    @Override
    public boolean supports(Class<?> arg0) {
        return true;
    }

}